﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Documents;

namespace SDF
{
    [ContentProperty("Blocks")]
    public class SmartDocument : SmartTextElement, IBlockContainer, IDocumentPaginatorSource
    {
        #region Constructor

        public SmartDocument()
        {
            Content = new PageContent();
            this.Background = Brushes.White;
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty ColumnWidthProperty = 
            System.Windows.Documents.FlowDocument.ColumnWidthProperty.AddOwner(typeof(SmartDocument));

        public static readonly DependencyProperty ColumnGapProperty =
            System.Windows.Documents.FlowDocument.ColumnGapProperty.AddOwner(typeof(SmartDocument));

        public static readonly DependencyProperty IsColumnWidthFlexibleProperty =
            System.Windows.Documents.FlowDocument.IsColumnWidthFlexibleProperty.AddOwner(typeof(SmartDocument));

        public static readonly DependencyProperty ColumnRuleWidthProperty =
            System.Windows.Documents.FlowDocument.ColumnRuleWidthProperty.AddOwner(typeof(SmartDocument));

        public static readonly DependencyProperty ColumnRuleBrushProperty =
            System.Windows.Documents.FlowDocument.ColumnRuleBrushProperty.AddOwner(typeof(SmartDocument));

        public static readonly DependencyProperty MarginProperty = DependencyProperty.Register(
            "Margin",
            typeof(Thickness),
            typeof(SmartDocument),
            new FrameworkPropertyMetadata(new Thickness(double.NaN)),
            new ValidateValueCallback(ValidateMargin));

        private static bool ValidateMargin(object value)
        {
            var thickness = (Thickness)value;
            if (double.IsInfinity(thickness.Bottom) ||
                double.IsInfinity(thickness.Left) ||
                double.IsInfinity(thickness.Right) ||
                double.IsInfinity(thickness.Top))
                return false;

            if (thickness.Bottom < 0 ||
                thickness.Left < 0 ||
                thickness.Right < 0 ||
                thickness.Top < 0)
                return false;

            return true;
        }

        #endregion

        #region Properties

        public PageContent Content { get; private set; }

        public SmartBlockCollection Blocks 
        {
            get { return Content.Blocks; }
        }

        public Thickness ActualMargin { get; private set; }

        public Thickness Margin
        {
            get { return (Thickness)GetValue(MarginProperty); }
            set { SetValue(MarginProperty, value); }
        }

        public double ColumnWidth
        {
            get { return (double)GetValue(ColumnWidthProperty); }
            set { SetValue(ColumnWidthProperty, value); }
        }

        public double ColumnGap
        {
            get { return (double)GetValue(ColumnGapProperty); }
            set { SetValue(ColumnGapProperty, value); }
        }

        public double ColumnRuleWidth
        {
            get { return (double)GetValue(ColumnRuleWidthProperty); }
            set { SetValue(ColumnRuleWidthProperty, value); }
        }

        public Brush ColumnRuleBrush
        {
            get { return (Brush)GetValue(ColumnRuleBrushProperty); }
            set { SetValue(ColumnRuleBrushProperty, value); }
        }
        
        public bool IsColumnWidthFlexible
        {
            get { return (bool)GetValue(IsColumnWidthFlexibleProperty); }
            set { SetValue(IsColumnWidthFlexibleProperty, value); }
        }

        #endregion

        #region Layout Methods

        protected override Size MeasureCore(Size availableSize)
        {
            ActualMargin = CalculateActualMargin();

            Size constraint = availableSize.Subtract(ActualMargin);
            Content.Measure(constraint);
            return Content.DesiredSize.Add(ActualMargin);
        }

        protected override void ArrangeCore(Rect finalRect)
        {
            Rect contentRect = finalRect.Subtract(ActualMargin);
            Content.Arrange(contentRect);
            base.RenderSize = finalRect.Size;
        }

        public virtual Thickness CalculateActualMargin()
        {
            double left = Margin.Left;
            double top = Margin.Top;
            double right = Margin.Right;
            double bottom = Margin.Bottom;

            if (double.IsNaN(left))
                left = LineHeight;
            if (double.IsNaN(top))
                top = LineHeight;
            if (double.IsNaN(right))
                right = LineHeight;
            if (double.IsNaN(bottom))
                bottom = LineHeight;

            return new Thickness(left, top, right, bottom);
        }

        #endregion

        #region PropertyChanged

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == SmartTextElement.BackgroundProperty)
            {
                Content.Background = this.Background;
            }
            else if (e.Property == SmartTextElement.FontFamilyProperty)
            {
                Content.FontFamily = this.FontFamily;
            }
            else if (e.Property == SmartTextElement.FontSizeProperty)
            {
                Content.FontSize = this.FontSize;
            }
            else if (e.Property == SmartTextElement.FontStretchProperty)
            {
                Content.FontStretch = this.FontStretch;
            }
            else if (e.Property == SmartTextElement.FontStyleProperty)
            {
                Content.FontStyle = this.FontStyle;
            }
            else if (e.Property == SmartTextElement.FontWeightProperty)
            {
                Content.FontWeight = this.FontWeight;
            }
            else if (e.Property == SmartTextElement.ForegroundProperty)
            {
                Content.Foreground = this.Foreground;
            }
            else if (e.Property == SmartTextElement.TextAlignmentProperty)
            {
                Content.TextAlignment = this.TextAlignment;
            }

        }

        #endregion

        #region Visual Children Implementation

        protected override int VisualChildrenCount
        {
            get{ return 1; }
        }

        protected override Visual GetVisualChild(int index)
        {
            if (index < 0 || index > 1)
                throw new ArgumentOutOfRangeException("index");

            return Content;
        }

        #endregion

        #region IDocumentPaginatorSource Implementation

        public DocumentPaginator DocumentPaginator
        {
            get { return new SmartDocumentPaginator(this); }
        }

        #endregion
    }
}
